#include "QuakeServer.h"
#include <ace/INET_Addr.h>
#include <ace/init_Ace.h>
#include <ace/OS.h>
#include <sstream>
#include <iostream>

#include "Buffer.h"
#include "Demo.h"
#include "Message.h"


float				m_CameraOrigin[3]={0};
float				m_CameraAngles[3]={0};


QuakeServer::QuakeServer(Demo &D,Script& S)
 : m_Demo(D),m_Script(S),m_SentOrder(0)
{
/*	for (int I = 0; I < 3; I++)
		m_CameraOrigin[I] = m_CameraAngles[I] = 0;*/
}


bool QuakeServer::AcceptClient()
{
	ACE_SOCK_Dgram FirstClientsSocket;

	if (-1 == FirstClientsSocket.open(ACE_INET_Addr(26000)))
		return false;

	try
	{
		while (true)
		{
			Buffer Request(2000);
			Buffer Response(2000);

			unsigned short	Type;
			unsigned short	Length;

			Request.SetUsedSize(
				FirstClientsSocket.recv(Request.GetBuffer(),Request.GetMaxSize(),m_ClientAddress));
			if (Request.GetUsedSize() <= 0)
				return 0;

			Type = Request.GetShort();
			if (Type != 0x0080)
				continue;

			Length = Request.GetShort();
			byte Code = Request.GetByte();
			

			std::string Name;
			byte		Version;
			switch (Code)
			{
			case 0x01: // Connection Request
			{
				Name = Request.GetString();
				Version = Request.GetByte();
				if (Name != "QUAKE" || Version != 3)
					continue;

				ACE_INET_Addr* pAddrs;
				size_t Count;
				if (ACE::get_ip_interfaces(Count,pAddrs) < 0 || Count < 1)
					return false;

				// create a personal port
				ACE_INET_Addr Local;
				int Index = 0;
				if (pAddrs[0].get_ip_address() == 0x7F000001)
					Index++;
				m_ClientSocket.open(pAddrs[Index]);
				m_ClientSocket.get_local_addr(Local);
				delete[] pAddrs;

				// respond with accept
				Response.PutShort(0x8000,true);
				Response.PutShort(0x09); 
				Response.PutByte(0x81); // Accept Connection
				Response.PutLong(Local.get_port_number());
				Response.ChangePos(2);
				Response.PutShort(Response.GetUsedSize(),true);
				FirstClientsSocket.send(Response.GetBuffer(),Response.GetUsedSize(),m_ClientAddress);
				if (g_Verbal)
					std::cout << "Accepting client to port " << Local.get_port_number() << std::endl;
				return true;

			}
			break;
			case 0x02: // Request Server Information
				Name = Request.GetString();
				Version = Request.GetByte();
				if (Name != "QUAKE" || Version != 3)
					continue;
				Response.PutShort(0x80,true);
				Response.PutShort(0); // Skip the length for now
				Response.PutByte(0x83); // Give Server Informations
				Response.PutString("localhost"); 
				Response.PutString("RemStud2"); 
				Response.PutString("current map");  
				Response.PutByte(0);  
				Response.PutByte(8);  
				Response.PutByte(3);  
				Response.ChangePos(2);
				Response.PutShort(Response.GetUsedSize(),true);
				FirstClientsSocket.send(Response.GetBuffer(),Response.GetUsedSize(),m_ClientAddress);

				break;
			default:
				continue;
			}

		}
	}
	catch (const char* pMsg)
	{
		if (g_Verbal)
			std::cout << pMsg << std::endl;
		return false;
	}
	catch (...)
	{
		if (g_Verbal)
			std::cout << "Cought unexpected exception" << std::endl;
		return false;
	}

	return true;
}


void QuakeServer::SendReliableBlock()
{
	Block& CurBlock = m_Demo.GetCurrentBlock();
	CurBlock.Unparse();
	do
	{
		Buffer Response(20000);

		if (CurBlock.GetRemainingSize() > 1024)
		{
			Response.PutShort(0x01,true); // incomplete
			Response.PutShort(0); // Skip the length for now
			Response.PutLong(m_SentOrder++,true); 

			CurBlock.WriteTo(Response,1024);
			Response.ChangePos(2);
			Response.PutShort(Response.GetUsedSize(),true);
			m_ClientSocket.send(Response.GetBuffer(),Response.GetUsedSize(),m_ClientAddress);

		}
		else
		{
			Response.PutShort(0x09,true); // last part
			Response.PutShort(0); // Skip the length for now
			Response.PutLong(m_SentOrder++,true); 

			CurBlock.WriteTo(Response);
			Response.ChangePos(2);
			Response.PutShort(Response.GetUsedSize(),true);
			m_ClientSocket.send(Response.GetBuffer(),Response.GetUsedSize(),m_ClientAddress);

			break;
		}

	} while (true);
}


void QuakeServer::SendUnreliableBlock()
{

	Buffer Response(20000);
	Response.PutShort(0x10,true);
	Response.PutShort(0); // Skip the length for now

	Response.PutLong(m_SentOrder++,true); 

	Block& CurBlock = m_Demo.GetCurrentBlock();

	CurBlock.Unparse();
	CurBlock.Reset();
	CurBlock.WriteTo(Response);

	// updateentity for the camera
	MessageUpdateEntity Upd(449,m_CameraOrigin,m_CameraAngles);
	Upd.WriteTo(Response);

	if (m_SnapToRoute)
	{
		// angles for the camera
		MessageSetangles Ang;
		for (int I =0; I < 3; I++)
			Ang.SetAng(I,m_CameraAngles[I]);
		Ang.WriteTo(Response);
	}

	std::stringstream  S;
	if (m_SnapToRoute)
	{
		if (m_Script.IsPoint(m_Time))
		{
			S << "(+)\n";
		}
		else
		{
			int Count = (int)(m_Script.GetRatio(m_Time) * 30 + 0.5);
			if (Count >= 0)
			{
				int I;
				S << '[';
				for (I = 0; I < Count; I++)
					S << '-';
				S << '+';
				for (I = 0; I < 30 - Count; I++)
					S << '-';
				S << "]\n";
			}
		}
	}


	float Time = CurBlock.GetBlockTime();

	if (Time > 60)
	{
		S << int(Time) / 60 << ":";
		S.fill('0');
		S.width(2);
	}

	S << int(Time) % 60 << ".";
	S << int(Time*100) % 100;
	MessageCenterprint Cnt(S.str());
	Cnt.WriteTo(Response);
	
	
	Response.ChangePos(2);
	Response.PutShort(Response.GetUsedSize(),true);

	m_ClientSocket.send(Response.GetBuffer(),Response.GetUsedSize(),m_ClientAddress);


}

bool QuakeServer::GetClientMessage(ClientCommand& Cmd,bool Wait)
{
	Buffer Request(20000);

	int Ret;

	if (Wait)
	{
		ACE_Time_Value TV(50,0);
		Ret = m_ClientSocket.recv(Request.GetBuffer(),Request.GetMaxSize(),m_ClientAddress,0,&TV);
	}
	else
	{
		Ret = m_ClientSocket.recv(Request.GetBuffer(),Request.GetMaxSize(),m_ClientAddress);
	}

	if (Ret != -1)
	{
		Request.SetUsedSize(Ret);
		
		int Type = Request.GetShort(true);
		int Length = Request.GetShort(true);
		int ReceivedOrder = Request.GetLong(true);
		int code = Request.GetByte();


		switch (code)
		{		
		case 0: // NOP
			break;
		case 1: //keepalive
			break;
		case 2: // disconnect
			throw "got disconnect from a client";
			break;
		case 3: // client movement
			{
				Cmd.HasMovement = true;
				int I;
				Request.GetFloat();  // time

				// angles
				for (I = 0; I < 3; I++)
					Cmd.Angles[I] = Request.GetByte() * 360.0 / 256;

				for (I = 0; I < 3; I++)
					Cmd.Speed[I] = (short)Request.GetShort(); // speed


				Cmd.Flags = Request.GetByte();
				Cmd.Impulse = Request.GetByte();

			}
			break;
		case 4: // console order
			Cmd.ConsoleCommand = Request.GetString();
			while (Request.HasMore())
			{
				Request.GetByte();
				std::string S = Request.GetString();
				if (S.size())
					Cmd.ConsoleCommand = S;
			}
			break;
		default:;
		
		}

		if (Type == 0x09 || Type == 0x01) // reliable
		{
			Buffer Ack(20);
			Ack.PutShort(0x02,true);
			Ack.PutShort(8,true); // the length is constant
			Ack.PutLong(ReceivedOrder,true); 
			m_ClientSocket.send(Ack.GetBuffer(),Ack.GetUsedSize(),m_ClientAddress);
		}

		return true;

	}
	else if (Wait)
	{
		throw "time out waiting for client message";
	}
	return false;
}

bool QuakeServer::RunEditing()
{
	try
	{
		BeginSession();
		m_Time = 1.4f;

		float EndTime=1.5;
		m_Script.GotoNextTime(EndTime);

		if (m_Demo.GetLastTime() > (EndTime*2))
		{
			// we have a script
			m_Speed = 2;
			m_SnapToRoute = true; 
		}
		else
		{
			m_Speed = 0;
			m_SnapToRoute = false;
		}

		while (true)
		{
/*			ACE_Time_Value V(0,1);
			ACE_OS::sleep(V);*/
			
			SendUnreliableBlock();

			ClientCommand Cmd;
			bool Res = GetClientMessage(Cmd,false);
			
			if (Res)
			{
				if (Cmd.HasMovement)
				{

					if (Cmd.Speed[0] != 0 || Cmd.Speed[1] != 0 || Cmd.Speed[2] != 0)
					{
						m_SnapToRoute = false;
					}

					if (!m_SnapToRoute)
					{
						float Yawn = Cmd.Angles[1]*3.1415 / 180;
						float Pitch = Cmd.Angles[0]*3.1415 / 180;

						float SYawn = sin(Yawn);
						float CYawn = cos(Yawn);

						float SPitch = sin(Pitch);
						float CPitch = cos(Pitch);
						float Delta[3];


						Delta[0] = CPitch * (( Cmd.Speed[0] * CYawn + Cmd.Speed[1] * SYawn));
						Delta[1] = CPitch * ((-Cmd.Speed[1] * CYawn + Cmd.Speed[0] * SYawn));
						Delta[2] = SPitch * -Cmd.Speed[0] + Cmd.Speed[2];


						static ACE_Time_Value LastMovementTime(0);
						ACE_Time_Value CurrTime = ACE_OS::gettimeofday();
						int Mul = (CurrTime - LastMovementTime).msec();
						if (Mul > 1000)
							Mul = 10;
						LastMovementTime = CurrTime;

						for (int I = 0; I < 3; I++)
						{
							m_CameraAngles[I] = Cmd.Angles[I];
							m_CameraOrigin[I] += Delta[I] * Mul / 300;
						}

						if (Cmd.Flags & 1)
						{
							m_Script.SetPoint(m_Time,m_CameraOrigin,m_CameraAngles);
							m_SnapToRoute = true;
						}
					}

			
				}

				switch (Cmd.Impulse)
				{
				case 100:
					m_SnapToRoute = !m_SnapToRoute;
					break;
				case 101:
					{		
						if (m_Time < 1)
							break;
						m_Script.GotoPrevTime(m_Time);
						m_Demo.SnapToTime(m_Time);
						m_SnapToRoute = true;
						m_Speed = 0;
					}
					break;
				case 102:
					{
						m_Script.GotoNextTime(m_Time);
						m_Demo.SnapToTime(m_Time);
						m_SnapToRoute = true;
						m_Speed = 0;
					}
					break;
				case 103:
					m_Script.RemovePoint(m_Time);
					m_SnapToRoute = true;
					break;
				case 200:
				case 201:
				case 202:
				case 203:
				case 204:
				case 205:
				case 206:
					m_Speed = Cmd.Impulse - 203; // -3..3
					break;
				}

			}
			m_Demo.Progress(m_Speed,m_Time);
			if (m_SnapToRoute)
			{
				m_Script.UpdateCamera(m_Time,m_CameraOrigin,m_CameraAngles);
			}
		}
	}

	catch (const char* pMsg)
	{
		if (g_Verbal)
			std::cout << pMsg << std::endl;
	}
	catch (...)
	{
		if (g_Verbal)
			std::cout << "Cought unexpected exception" << std::endl;
		return false;
	}


	return false;
}

void QuakeServer::BeginSession()
{
	ACE_OS::sleep(1);
	int Speed = 2;

	SendReliableBlock(); 
	m_Demo.Progress(Speed,m_Time,false);

	while (true)
	{
		ClientCommand Cmd;
		bool Res = GetClientMessage(Cmd,true);

		if (Res  && Cmd.ConsoleCommand == "prespawn")
			break;			
	}

	SendReliableBlock(); 
	m_Demo.Progress(Speed,m_Time,false);

	while (true)
	{
		ClientCommand Cmd;
		bool Res = GetClientMessage(Cmd,true);

		if (Res  && Cmd.ConsoleCommand == "spawn ")
			break;
	}

	SendReliableBlock(); 
	m_Demo.Progress(Speed,m_Time,false);

	while (true)
	{
		ClientCommand Cmd;
		bool Res = GetClientMessage(Cmd,true);

		if (Res && Cmd.ConsoleCommand == "begin")
			break;
	}

	m_Demo.MarkFirstBlock();
	m_SentOrder = 0;
}







ClientCommand::ClientCommand()
{
	HasMovement = false;
	for (int I = 0; I < 3; I++)
		Speed[I] = Angles[I] = 0;
	
	Impulse = 0;
}

